#pragma once

#include <AVUIIXamlResolutionContext.h>
#include <AVUIIAddChild.h>
#include <AVUIISealable.h>
#include <AVUIIHaveResources.h>
#include <AVUINameScope.h>
#include <AVUIStyleStructures.h>
#include <AVUIFrugalMap.h>

namespace AVUI {
class HybridDictionary;
class ResourceDictionary;
class SetterBaseCollection;
class TriggerCollection;
class NameScope;
class FrameworkElement;

class Style : public DispatcherObject, public IXamlResolutionContext, public INameScope, public IAddChild, public ISealable, public IHaveResources
{
public:
    DECLARE_ELEMENT(Style, DispatcherObject);

#pragma region Public Methods
    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Style" /> class. </summary>
    Style();

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Style" /> class to use on the specified <see cref="T:System.Type" />. </summary>
    /// <param name="targetType">The type to which the style will apply.</param>
    Style(const Type* pTargetType);

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Style" /> class to use on the specified <see cref="T:System.Type" /> and based on the specified <see cref="T:System.Windows.Style" />. </summary>
    /// <param name="targetType">The type to which the style will apply.</param>
    /// <param name="basedOn">The style to base this style on.</param>
    Style(Type* pTargetType, Style* pBasedOn);

    /// <summary>Returns the hash code for this <see cref="T:System.Windows.Style" />.    </summary>
    /// <returns>The hash code for this <see cref="T:System.Windows.Style" />.   </returns>
    virtual int GetHashCode() AVUI_OVERRIDE { VerifyAccess(); return GlobalIndex; }

    /// <summary>Registers a new name-object pair in the current namescope.</summary>
    /// <returns />
    /// <param name="name">The name to register.</param>
    /// <param name="scopedElement">The object to map to the specified <paramref name="name" />.</param>
    void RegisterName(String* pName, Object* pScopedElement);

    /// <summary>Locks this style and all factories and triggers so they cannot be changed.</summary>
    void Seal();
 
    /// <summary>Removes a name-object mapping from the namescope.</summary>
    /// <returns />
    /// <param name="name">The name of the mapping to remove.</param>
    void UnregisterName(String* pName);

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets or sets a defined style that is the basis of the current style.</summary>
    /// <returns>A defined style that is the basis of the current style. The default value is null.</returns>
    Style* get_BasedOn() { VerifyAccess(); return m_pBasedOn; }
    void set_BasedOn(Style* pValue);
    DECLARE_PROPERTY(BasedOn, Style, false);
 
    /// <summary>Gets a value that indicates whether the style is read-only and cannot be changed.</summary>
    /// <returns>true if the style is sealed; otherwise false.</returns>
    bool get_IsSealed() { VerifyAccess(); return m_sealed; }
    DECLARE_READONLY_PROPERTY(IsSealed, bool, false);

    /// <summary>Gets or sets the collection of resources that can be used within the scope of this style.</summary>
    /// <returns>The resources that can be used within the scope of this style.</returns>
    ResourceDictionary* get_Resources();
    void set_Resources(ResourceDictionary* pValue);
    DECLARE_PROPERTY(Resources, ResourceDictionary, false);

    /// <summary>Gets a collection of <see cref="T:System.Windows.Setter" /> and <see cref="T:System.Windows.EventSetter" /> objects.</summary>
    /// <returns>a collection of <see cref="T:System.Windows.Setter" /> and <see cref="T:System.Windows.EventSetter" /> objects. The default is an empty collection.</returns>
    SetterBaseCollection* get_Setters();
    DECLARE_READONLY_PROPERTY(Setters, SetterBaseCollection, true);

    /// <summary>Gets or sets the type for which this style is intended.</summary>
    /// <returns>The target type for this style.</returns>
    const Type* get_TargetType() { VerifyAccess(); return m_pTargetType; }
    void set_TargetType(const Type* pValue);
    DECLARE_PROPERTY(TargetType, Type, false);
 
    /// <summary>Gets a collection of <see cref="T:System.Windows.TriggerBase" /> objects that apply property values based on specified conditions.</summary>
    /// <returns>A collection of <see cref="T:System.Windows.TriggerBase" /> objects. The default is an empty collection.</returns>
    TriggerCollection* get_Triggers();
    DECLARE_READONLY_PROPERTY(Triggers, TriggerCollection, false);

#pragma endregion Public Properties

#pragma region ISealable

    virtual bool get_CanSeal() AVUI_OVERRIDE { return true; }

#pragma endregion ISealable

#pragma region IAddChild

    virtual void AddChild(Object* pValue) AVUI_OVERRIDE;
    virtual void AddText(String* pText) AVUI_OVERRIDE;

#pragma endregion IAddChild

#pragma region INameScope

    virtual Object* FindName(String* pName) AVUI_OVERRIDE;
    virtual Object* get_Object() AVUI_OVERRIDE { return this; }

#pragma endregion INameScope

#pragma region WPFG/Deviation, IXamlResolutionContext

    virtual const Type* GetPropertyResolutionContext(Object* pObjectCurrent) AVUI_OVERRIDE;
    virtual Property* GetValueConverterContext(Object* pObjectCurrent) AVUI_OVERRIDE;

#pragma endregion WPFG/Deviation, IXamlResolutionContext

public:

#pragma region Internal Methods

    void CheckTargetType(Object* pElement);
    TRefCountedPtr<Object> FindResource(Object* pResourceKey, bool allowDeferredResourceReference, bool mustReturnDeferredResourceReference);
    bool IsModified(int id) { return (id & m_modified) != 0; }

#pragma endregion Internal Methods

#pragma region Internal Properties

    EventHandlersStore* get_EventHandlersStore() { return m_pEventHandlersStore; }
    bool get_HasEventDependents() { return EventDependents.get_Count() > 0; }
    bool get_HasEventSetters() { return IsModified(0x10); }
    bool get_HasInstanceValues() { return m_hasInstanceValues; }

    bool get_HasLoadedChangeHandler() { return m_hasLoadedChangeHandler; }
    void set_HasLoadedChangeHandler(bool value) { m_hasLoadedChangeHandler = value; }

    bool get_HasResourceReferences() { return ResourceDependents.get_Count() > 0; }
    bool get_IsBasedOnModified() { return IsModified(2); }

#pragma endregion Internal Properties

#pragma region Internal Fields

    TRefCountedPtr<HybridDictionary> m_pDataTriggerRecordFromBinding;
    TRefCountedPtr<ResourceDictionary> m_pResources;
    TRefCountedPtr<HybridDictionary> m_pTriggerActions;
    ItemStructList<ChildRecord> ChildRecordFromChildIndex;
    ItemStructList<ContainerDependent> ContainerDependents;
    TRefCountedPtr<HybridDictionary> DataTriggersWithActions;
    ItemStructList<ChildEventDependent> EventDependents;
    int GlobalIndex;
    FrugalMap PropertyTriggersWithActions;
    ItemStructList<PropertyValue> PropertyValues;
    ItemStructList<ChildPropertyDependent> ResourceDependents;
    ItemStructList< ItemStructMap<TriggerSourceRecord> > TriggerSourceRecordFromChildIndex;

#pragma endregion Internal Fields

private:

#pragma region Private Methods

    void CheckForCircularBasedOnReferences();
    void GetUniqueGlobalIndex();
    static bool IsEqual(Object* pA, Object* pB);
    void ProcessSelfStyles(Style* pStyle);
    void ProcessSetters(Style* pStyle);
    void ProcessVisualTriggers(Style* pStyle);
    void SetModified(int id) { m_modified |= id; };
    void UpdatePropertyValueList(DependencyProperty* pDP, PropertyValueType::Enum valueType, Object* pValue);
    static const Type* get_DefaultTargetType() { return &GET_TYPE(FrameworkElement); }

#pragma endregion Private Methods

#pragma region Private Fields

    TRefCountedPtr<Style> m_pBasedOn;
    TRefCountedPtr<EventHandlersStore> m_pEventHandlersStore;
    bool m_hasInstanceValues;
    bool m_hasLoadedChangeHandler;
    int m_modified;
    TRefCountedPtr<NameScope> m_pNameScope;
    bool m_sealed;
    TRefCountedPtr<SetterBaseCollection> m_pSetters;
    const Type* m_pTargetType;
    TRefCountedPtr<TriggerCollection> m_pVisualTriggers;
    static int StyleInstanceCount;

#pragma endregion Private Fields

};


}; // namespace AVUI
